/*
	造成伤害的方法
*/

package net.sf.odinms.net.channel.handler;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.sf.odinms.client.ISkill;
import net.sf.odinms.client.MapleBuffStat;
import net.sf.odinms.client.MapleCharacter;
import net.sf.odinms.client.MapleJob;
import net.sf.odinms.client.SkillFactory;
import net.sf.odinms.client.anticheat.CheatingOffense;
import net.sf.odinms.client.skills.*;
import net.sf.odinms.client.status.MonsterStatus;
import net.sf.odinms.client.status.MonsterStatusEffect;
import net.sf.odinms.net.AbstractMaplePacketHandler;
import net.sf.odinms.server.MapleStatEffect;
import net.sf.odinms.server.TimerManager;
import net.sf.odinms.server.life.Element;
import net.sf.odinms.server.life.ElementalEffectiveness;
import net.sf.odinms.server.life.MapleMonster;
import net.sf.odinms.server.maps.MapleMap;
import net.sf.odinms.server.maps.MapleMapItem;
import net.sf.odinms.server.maps.MapleMapObject;
import net.sf.odinms.server.maps.MapleMapObjectType;
import net.sf.odinms.server.maps.MapleSummon;
import net.sf.odinms.server.maps.SummonMovementType;
import net.sf.odinms.tools.MaplePacketCreator;
import net.sf.odinms.tools.Pair;
import net.sf.odinms.tools.WriteToFile;
import net.sf.odinms.tools.data.input.LittleEndianAccessor;


public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandler {

    private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractDealDamageHandler.class);
    public boolean savePacket = true; //是否记录攻击错误包
    
    //攻击信息
    protected class AttackInfo {
        //088需要声明个move
        public int numAttacked,  numDamage,  numAttackedAndDamage, move;
        public int skill,  stance,  direction,  charge,  pos, aranCombo;
        public List<Pair<Integer, List<Integer>>> allDamage;
        public boolean isHH = false;
        public int speed = 4;

        private MapleStatEffect getAttackEffect(MapleCharacter chr, ISkill theSkill) {//获取攻击效果
            ISkill mySkill = theSkill;
            if (mySkill == null) {
                mySkill = SkillFactory.getSkill(skill);
            }
            int skillLevel = chr.getSkillLevel(mySkill);
            if (mySkill.getId() == 1009 || mySkill.getId() == 10001009) {
                skillLevel = 1;
            }
            if (skillLevel == 0) {
                return null;
            }
            return mySkill.getEffect(skillLevel);
        }

        public MapleStatEffect getAttackEffect(MapleCharacter chr) {
            return getAttackEffect(chr, null);
        }
    }
    
    //应用攻击
    protected void applyAttack(AttackInfo attack, MapleCharacter player, int maxDamagePerMonster, int attackCount) { //应用攻击
        player.getCheatTracker().resetHPRegen();//玩家欺骗服务器重置HP？
        player.resetAfkTimer();
        player.getCheatTracker().checkAttack(attack.skill);
        ISkill theSkill = null;
        MapleStatEffect attackEffect = null;
        if (attack.skill != 0) { //如果攻击的技能不为0
            theSkill = SkillFactory.getSkill(attack.skill);
            attackEffect = attack.getAttackEffect(player, theSkill);
            if (attackEffect == null) { //如果没有攻击效果
                System.out.println(player.getName()+"使用了没有的技能- 技能ID: (" + attack.skill + ")");
                player.getClient().getSession().write(MaplePacketCreator.enableActions());
                return;
            }
            if (attack.skill != 牧师.群体治愈
                    && attack.skill != 战法.飓风
                    && attack.skill != 机械师.金属机甲_重机枪
                    && attack.skill != 机械师.金属机甲_导弹战车
                    && attack.skill != 机械师.金属机甲_重机枪_4转
                    && attack.skill != 机械师.火箭推进器
                    ) {
                if (player.isAlive()) {
                    //System.out.println("applyAttack的技能ID："+attack.skill);
                    //System.out.println("applyAttack的技能名字："+SkillFactory.getSkillName(attack.skill));
                    attackEffect.applyTo(player);
                } else {
                    player.getClient().getSession().write(MaplePacketCreator.enableActions());
                }
            }
        }
        if (!player.isAlive()) { //如果玩家死了
            player.getCheatTracker().registerOffense(CheatingOffense.ATTACKING_WHILE_DEAD);
            return;
        }
        int totDamage = 0;
        final MapleMap map = player.getMap();
        if (attack.skill == 4211006) { // 金钱炸弹
            int delay = 0;
            for (Pair<Integer, List<Integer>> oned : attack.allDamage) {
                MapleMapObject mapobject = map.getMapObject(oned.getLeft().intValue());
                if (mapobject != null && mapobject.getType() == MapleMapObjectType.ITEM) {
                    final MapleMapItem mapitem = (MapleMapItem) mapobject;
                    if (mapitem.getMeso() > 0) {
                        synchronized (mapitem) {
                            if (mapitem.isPickedUp()) {
                                return;
                            }
                            TimerManager.getInstance().schedule(new Runnable() {
                                public void run() {
                                    map.removeMapObject(mapitem);
                                    map.broadcastMessage(MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 4, 0), mapitem.getPosition());
                                    mapitem.setPickedUp(true);
                                }
                            }, delay);
                            delay += 100;
                        }
                    } else if (mapitem.getMeso() == 0) {
                        player.getCheatTracker().registerOffense(CheatingOffense.ETC_EXPLOSION);
                        return;
                    }
                } else if (mapobject != null && mapobject.getType() != MapleMapObjectType.MONSTER) {
                    player.getCheatTracker().registerOffense(CheatingOffense.EXPLODING_NONEXISTANT);
                    return; // etc explosion, exploding nonexistant things, etc.
                }
            }
        }

        for (Pair<Integer, List<Integer>> oned : attack.allDamage) {
            //这里开始的处理是单次攻击的
            MapleMonster monster = map.getMonsterByOid(oned.getLeft().intValue());
            if (monster != null) {
                if (!monster.isControllerHasAggro())
                    if (monster.getController() == player)
                        monster.setControllerHasAggro(true);
                    else
                        monster.switchController(player, true);

                int totDamageToOneMonster = 0;
                for (Integer eachd : oned.getRight()) {
                    totDamageToOneMonster += eachd.intValue();
                }
                totDamage += totDamageToOneMonster;
                player.checkMonsterAggro(monster);
                if (totDamageToOneMonster > attack.numDamage + 1) {
                    int dmgCheck = player.getCheatTracker().checkDamage(totDamageToOneMonster);
                    if (dmgCheck > 5 && totDamageToOneMonster < 99999 && monster.getId() < 9500317 && monster.getId() > 9500319) {
                        player.getCheatTracker().registerOffense(CheatingOffense.SAME_DAMAGE, dmgCheck + " times: " + totDamageToOneMonster);
                    }
                }
                checkHighDamage(player, monster, attack, theSkill, attackEffect, totDamageToOneMonster, maxDamagePerMonster);
                double distance = player.getPosition().distanceSq(monster.getPosition());
                if (distance > 400000.0) { // 600^2, 550 大约是普通远程攻击的距离
                    player.getCheatTracker().registerOffense(CheatingOffense.ATTACK_FARAWAY_MONSTER, Double.toString(Math.sqrt(distance)));
                }

                if (attack.skill == 拳手.能量耗转
                        || attack.skill == 弩骑.利爪狂风
                        || attack.skill == 标飞.生命吸收
                        || attack.skill == 夜行者.吸血
                        || player.getBuffedValue(MapleBuffStat.连环吸血) != null) {
                    //能量耗转 利爪狂风 生命吸收
                    int skillid = attack.skill;
                    if(player.getBuffedValue(MapleBuffStat.连环吸血) != null)
                        skillid = 21100005;
                    ISkill skill = SkillFactory.getSkill(skillid);
                    int gainhp = (int) ((double) totDamage * (double) skill.getEffect(player.getSkillLevel(skillid)).getX() / 100.0);
                    if(attack.skill == 弩骑.利爪狂风) //利爪狂风
                        gainhp = Math.min(monster.getMaxHp(), Math.min(gainhp, player.getMaxHp() / 6)); //15%
                    else if (player.getBuffedValue(MapleBuffStat.连环吸血) != null)
                        gainhp = Math.min(monster.getMaxHp(), Math.min(gainhp, player.getMaxHp() / 10)); //10%
                    else
                        gainhp = Math.min(monster.getMaxHp(), Math.min(gainhp, player.getMaxHp() / 2)); //50%
                    player.addHP(gainhp);
                } else if ((attack.skill == 4001334 || attack.skill == 4201005 || attack.skill == 0 || attack.skill == 4211002 || attack.skill == 4211004) && player.getBuffedValue(MapleBuffStat.PICKPOCKET) != null) {
                    handlePickPocket(player, monster, oned);
                } else if (attack.skill == 牧师.群体治愈 && !monster.getUndead()) { //群体治愈
                    player.getCheatTracker().registerOffense(CheatingOffense.HEAL_ATTACKING_UNDEAD);
                    return;
                }

                // x是减益值 y是持续时间
                if (player.getBuffedValue(MapleBuffStat.HAMSTRING) != null) { //缓速箭
                    ISkill hamstring = SkillFactory.getSkill(弓手.缓速箭);
                    if (hamstring.getEffect(player.getSkillLevel(hamstring)).makeChanceResult()) {
                        MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.SPEED, hamstring.getEffect(player.getSkillLevel(hamstring)).getX()), hamstring, false);
                        monster.applyStatus(player, monsterStatusEffect, false, hamstring.getEffect(player.getSkillLevel(hamstring)).getY() * 1000);
                    }
                } else if (player.getBuffedValue(MapleBuffStat.BLIND) != null) { //刺眼箭  弩骑-致盲
                    ISkill blind = SkillFactory.getSkill(弩手.刺眼箭);
                    if(player.getSkillLevel(blind) == 0)
                        blind = SkillFactory.getSkill(弩骑.致盲); //致盲
                    if (blind.getEffect(player.getSkillLevel(blind)).makeChanceResult()) {
                        MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.ACC, blind.getEffect(player.getSkillLevel(blind)).getX()), blind, false);
                        monster.applyStatus(player, monsterStatusEffect, false, blind.getEffect(player.getSkillLevel(blind)).getY() * 1000);
                    }
                } else if (player.getBuffedValue(MapleBuffStat.抗压) != null) { //抗压
                    ISkill blind = SkillFactory.getSkill(战神.抗压);
                    if (blind.getEffect(player.getSkillLevel(blind)).makeChanceResult()) {
                        MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.抗压, blind.getEffect(player.getSkillLevel(blind)).getX()), blind, false);
                        monster.applyStatus(player, monsterStatusEffect, false, blind.getEffect(player.getSkillLevel(blind)).getY() * 1000);
                    }
                }

                if (player.getJob().isA(MapleJob.WHITEKNIGHT) || (player.isAran() && player.getBuffedValue(MapleBuffStat.WK_CHARGE) != null)) {
                    //寒冰冲击 冰雪矛
                    int[] charges = new int[]{骑士.寒冰冲击, 战神.冰雪矛};
                    for (int charge : charges) {
                        ISkill chargeSkill = SkillFactory.getSkill(charge);
                        int skilllevel = player.getSkillLevel(chargeSkill);
                        if(skilllevel == 0)
                            continue;
                        MapleStatEffect chargeEffect = chargeSkill.getEffect(skilllevel);
                        //判断这个buff是否来自charge这个技能
                        if (player.isBuffFrom(MapleBuffStat.WK_CHARGE, chargeSkill)) {
                            if (totDamageToOneMonster > 0) {
                                MonsterStatusEffect monsterStatusEffect;
                                if(charge == 骑士.寒冰冲击){
                                    //获取怪物对冰属性的抗性
                                    ElementalEffectiveness iceEffectiveness = monster.getEffectiveness(Element.ICE);
                                    //属性不是 抗冰 或者 免疫冰
                                    if(iceEffectiveness != ElementalEffectiveness.IMMUNE && iceEffectiveness != ElementalEffectiveness.STRONG) {
                                        monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.FREEZE, 1), chargeSkill, false);
                                        //System.out.println("寒冰冲击发动");
                                    } else {
                                        //System.out.println("寒冰冲击由于怪物属性抗冰而没有发动");
                                        break;
                                    }
                                } else { //技能是冰雪矛
                                    //System.out.println("冰雪矛发动");
                                    monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.SPEED, 1), chargeSkill, false);
                                }
                                monster.applyStatus(player, monsterStatusEffect, false, chargeEffect.getY() * 2000);
                                break;
                            }
                        }
                    }
                }

                if (player.getBuffedValue(MapleBuffStat.重生) != null && totDamage > monster.getHp()) { //幻灵 重生
                    ISkill chongsheng = SkillFactory.getSkill(战法.重生);
                    if (chongsheng.getEffect(player.getSkillLevel(chongsheng)).makeChanceResult()) {
                        System.out.println("重生效果发动");
                        MapleSummon summon = player.getSummon(战法.重生);
                        if (summon != null) {
                            player.getMap().broadcastMessage(MaplePacketCreator.removeSpecialMapObject(summon, true));
                            player.getMap().removeMapObject(summon);
                            player.removeVisibleMapObject(summon);
                            player.removeSummon(战法.重生);
                        }
                        MapleSummon tosummon = new MapleSummon(player, 战法.重生, player.getPosition(), SummonMovementType.UNKNOWN);
                        player.getMap().spawnSummon(tosummon);
                        //player.getSummons().put(32111006, tosummon);
                        player.putSummon(战法.重生, tosummon);
                    }
                }
                int skillid = 0;
                    if(player.getJob().getId() == 412)
                        skillid = 标飞.武器用毒液;
                    else if(player.getJob().getId() == 412)
                        skillid = 刀飞.武器用毒液;
                    else if(player.getJob().getId() == 434)
                        skillid = 双刀.武器用毒液;
                    else if(player.getJob().getId() == 1411)
                        skillid = 夜行者.武器用毒液;
                    ISkill venomNL = SkillFactory.getSkill(skillid);
                if (player.getSkillLevel(venomNL) > 0) {
                    MapleStatEffect venomEffect = venomNL.getEffect(player.getSkillLevel(venomNL));
                    for (int i = 0; i < attackCount; i++) {
                        if (venomEffect.makeChanceResult() == true) {
                            if (monster.getVenomMulti() < 3) {
                                monster.setVenomMulti(monster.getVenomMulti() + 1);
                                MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.POISON, 1), venomNL, false);
                                monster.applyStatus(player, monsterStatusEffect, false, venomEffect.getDuration(), true);
                            }
                        }
                    }
                }
                //给怪物BUFF
                if (totDamageToOneMonster > 0 && attackEffect != null && attackEffect.getMonsterStati().size() > 0) {
                    if (attackEffect.makeChanceResult()) {
                        MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(attackEffect.getMonsterStati(), theSkill, false);
                        monster.applyStatus(player, monsterStatusEffect, attackEffect.isPoison(), attackEffect.getDuration());
                    }
                }
                if (attack.isHH && !monster.isBoss()) {
                    map.damageMonster(player, monster, monster.getHp() - 1);
                } else {
                    map.damageMonster(player, monster, totDamageToOneMonster);
                }
                    if (player.getBuffedValue(MapleBuffStat.DARKSIGHT) != null && player.getJob().isA(MapleJob.Dual_Blade_4)) {
                    final ISkill skill = SkillFactory.getSkill(双刀.进阶隐身术);
                    final int skillLevel = player.getSkillLevel(双刀.进阶隐身术);
                    if (skillLevel == 0 || !skill.getEffect(skillLevel).makeChanceResult()) {
                        player.cancelBuffStats(MapleBuffStat.DARKSIGHT);
                    }
                }
            }
        }
        if (totDamage > 1) {
            player.getCheatTracker().setAttacksWithoutHit(player.getCheatTracker().getAttacksWithoutHit() + 1);
            final int offenseLimit;
            if (attack.skill != 弓手.暴风箭雨) {//不是暴风箭雨
                offenseLimit = 100;
            } else {
                offenseLimit = 300;
            }
            if (player.getCheatTracker().getAttacksWithoutHit() > offenseLimit) {
                player.getCheatTracker().registerOffense(CheatingOffense.ATTACK_WITHOUT_GETTING_HIT, Integer.toString(player.getCheatTracker().getAttacksWithoutHit()));
            }
            if (player.hasEnergyCharge()) {
                //增加能量 Miss不增加
                player.increaseEnergyCharge(attack.numAttacked);
            }
        }
        
    }

    //偷盗技能
    private void handlePickPocket(MapleCharacter player, MapleMonster monster, Pair<Integer, List<Integer>> oned) { //敛财术
        ISkill pickpocket = SkillFactory.getSkill(刀飞.敛财术);//敛财术
        int delay = 0;
        int maxmeso = player.getBuffedValue(MapleBuffStat.PICKPOCKET).intValue();
        int reqdamage = 20000;
        Point monsterPosition = monster.getPosition();

        for (Integer eachd : oned.getRight()) {
            if (pickpocket.getEffect(player.getSkillLevel(pickpocket)).makeChanceResult()) {
                double perc = (double) eachd / (double) reqdamage;
                final int todrop = Math.min((int) Math.max(perc * (double) maxmeso, (double) 1), maxmeso);
                final MapleMap tdmap = player.getMap();
                final Point tdpos = new Point((int) (monsterPosition.getX() + (Math.random() * 100) - 50), (int) (monsterPosition.getY()));
                final MapleMonster tdmob = monster;
                final MapleCharacter tdchar = player;
                TimerManager.getInstance().schedule(new Runnable() {
                    public void run() {
                        tdmap.spawnMesoDrop(todrop, tdpos, tdmob, tdchar, false);
                    }
                }, delay);
                delay += 200;
            }
        }
    }

    //检查高伤害
    private void checkHighDamage(MapleCharacter player, MapleMonster monster, AttackInfo attack, ISkill theSkill, MapleStatEffect attackEffect, int damageToMonster, int maximumDamageToMonster) { //检查高攻击伤害
        int elementalMaxDamagePerMonster;
        Element element = Element.PHYSICAL;
        if (theSkill != null) {
            element = theSkill.getElement();
            int skillId = theSkill.getId();
            if (skillId == 弩手.一击要害箭) {
                maximumDamageToMonster = 99999;
            } else if (skillId == 刀飞.暗杀) {
                maximumDamageToMonster = 400000;
            }
        }
        if (player.getBuffedValue(MapleBuffStat.WK_CHARGE) != null) {
            int chargeSkillId = player.getBuffSource(MapleBuffStat.WK_CHARGE);
            switch (chargeSkillId) {
                case 1211003:
                case 1211004:
                    element = Element.FIRE;
                    break;
                case 1211005:
                case 1211006:
                    element = Element.ICE;
                    break;
                case 1211007:
                case 1211008:
                    element = Element.LIGHTING;
                    break;
                case 1221003:
                case 1221004:
                    element = Element.HOLY;
                    break;
            }
            ISkill chargeSkill = SkillFactory.getSkill(chargeSkillId);
            maximumDamageToMonster *= chargeSkill.getEffect(player.getSkillLevel(chargeSkill)).getDamage() / 100.0;
        }
        if (element != Element.PHYSICAL) {
            double elementalEffect;
            if (attack.skill == 弩手.寒冰箭 || attack.skill == 弓手.烈火箭) { // inferno and blizzard
                elementalEffect = attackEffect.getX() / 200.0;
            } else {
                elementalEffect = 0.5;
            }
            switch (monster.getEffectiveness(element)) {
                case IMMUNE:
                    elementalMaxDamagePerMonster = 1;
                    break;
                case NORMAL:
                    elementalMaxDamagePerMonster = maximumDamageToMonster;
                    break;
                case WEAK:
                    elementalMaxDamagePerMonster = (int) (maximumDamageToMonster * (1.0 + elementalEffect));
                    break;
                case STRONG:
                    elementalMaxDamagePerMonster = (int) (maximumDamageToMonster * (1.0 - elementalEffect));
                    break;
                default:
                    throw new RuntimeException("Unknown enum constant");
            }
        } else {
            elementalMaxDamagePerMonster = maximumDamageToMonster;
        }
        if (damageToMonster > elementalMaxDamagePerMonster)
            player.getCheatTracker().registerOffense(CheatingOffense.HIGH_DAMAGE);
    }

    //反击(抗压 飓风等)
    public AttackInfo parsePassiveEnergy(MapleCharacter chr, LittleEndianAccessor lea) { //解析伤害数值
        //System.out.println("Energy 封包: "+lea.toString());
        AttackInfo ret = new AttackInfo();
        lea.readByte();
        lea.readLong();
        ret.numAttackedAndDamage = lea.readByte();
        lea.readLong();
        ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
        ret.numDamage = ret.numAttackedAndDamage & 0xF;
        ret.allDamage = new ArrayList<Pair<Integer, List<Integer>>>();
        ret.skill = changeSkilliId(lea.readInt());  //技能ID
        ret.move = lea.readByte();//移动
        //无论move == 0/1 前面都有这一段不知道什么意思的代码
        lea.skip(1); //01
        lea.skip(8); //不知道是什么信息

        if(ret.move != 0) {
            lea.skip(4); //00
            Odinms(lea);
            Moveme(lea);
            lea.skip(19);
            //System.out.println("move != 0");
        } else {
            //System.out.println("move == 0");
        }
        lea.skip(4);
        ret.aranCombo = lea.readByte(); //战神连击数
        ret.pos = lea.readByte(); //动作
        ret.stance = lea.readByte(); //姿势 一般0x80
        lea.readShort(); //092
        if(ret.move == 0)
            lea.readShort(); //093
        ret.speed = lea.readByte();//武器攻击速度
        if(chr.getCygnusBless())
            lea.skip(12); //3个相同的Int
        lea.readLong();//091我
        if(!isPartyBuff(ret.skill)) {
            ret.direction = lea.readByte();
        }
        /*
        System.out.println("连击数:"+ret.aranCombo);
        System.out.println("动作:"+ret.pos);
        System.out.println("姿势:"+ret.stance);
        System.out.println("速度:"+ret.speed);
        System.out.println("方向:"+ret.direction);
        */
        //这里是真正的攻击封包
        for (int i = 0; i < ret.numAttacked; i++) {
            int mobid = lea.readInt();
            //System.out.print("mobid: "+mobid+"\r\n");//怪物在地图的编号，并不是怪物ID
            lea.skip(14);
            List<Integer> allDamageNumbers = new ArrayList<Integer>();
            for (int j = 0; j < ret.numDamage; j++) {
                int damage = lea.readInt();//打怪的伤害
                if(damage < 0 || damage >= 999999) {
                    savePacket = true;
                    chr.黄字公告("您的数据出现异常,请联系Gm处理。");
                }
                //System.out.print("打怪伤害: "+damage+"\r\n");
                MapleStatEffect effect = null;
                if (ret.skill != 0) {
                    effect = SkillFactory.getSkill(ret.skill).getEffect(chr.getSkillLevel(SkillFactory.getSkill(ret.skill)));
                }
                if (damage != 0 && effect != null && effect.getFixedDamage() != 0) {
                    damage = effect.getFixedDamage();
                }
                allDamageNumbers.add(Integer.valueOf(damage));
            }
            lea.skip(8);
            ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(mobid), allDamageNumbers));
        }
        if(savePacket && !ret.allDamage.isEmpty()) {
            WriteToFile re = new WriteToFile("D:\\ErrorPacket\\EnergyError.txt");
            re.WriteFile("damage："+ret.allDamage
                    + "\r\npacket："+lea.toString()
                    + "\r\nplayerName："+chr.getName()
                    + "\r\nskillId："+ret.skill
                    + "\r\nskillName："+SkillFactory.getSkillName(ret.skill)
                    + "\r\n"
                    );
            re.CloseFile();
            savePacket = false;
        }
        return ret;
    }

    //远程攻击
    public AttackInfo parseRange(MapleCharacter chr, LittleEndianAccessor lea) { //解析伤害数值
        //System.out.println("Range 封包: "+lea.toString());
        AttackInfo ret = new AttackInfo();
        lea.readByte();
        lea.readLong();
        ret.numAttackedAndDamage = lea.readByte();
        lea.readLong();
        ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
        ret.numDamage = ret.numAttackedAndDamage & 0xF;
        ret.allDamage = new ArrayList<Pair<Integer, List<Integer>>>();
        ret.skill = changeSkilliId(lea.readInt());  //技能ID
        ret.move = lea.readByte();//移动
        //无论move == 0/1 前面都有这一段不知道什么意思的代码
        lea.skip(1); //01
        lea.skip(8); //不知道是什么信息

        if(ret.move != 0) {
            lea.skip(4); //00
            Odinms(lea);
            Moveme(lea);
            lea.skip(17);
            //System.out.println("move != 0");
        } else {
            //System.out.println("move == 0");
        }
        if (ret.skill == 弓手.暴风箭雨
                || ret.skill == 机械师.火焰喷射器
                || ret.skill == 枪手.投弹攻击
                || ret.skill == 夜行者.毒炸弹
                || ret.skill == 拳手.贯骨击
                || ret.skill == 奇袭者.贯骨击
                || ret.skill == 双刀.终极斩
                || ret.skill == 双刀.怪物炸弹
                || ret.skill == 枪手.金属风暴
                || ret.skill == 风灵使者.暴风箭雨
                || ret.skill == 弩骑.奥义箭乱舞
                ) {
            ret.charge = lea.readInt();
        } else {
            ret.charge = 0;
        }
        lea.skip(4);
        lea.skip(1);
        ret.aranCombo = lea.readByte(); //战神连击数
        ret.pos = lea.readByte(); //动作
        ret.stance = lea.readByte(); //姿势
        lea.readInt(); //092
        lea.readByte();//092
        if(chr.getCygnusBless())
            lea.skip(12); //3个相同的Int
        ret.speed = lea.readByte();//速度
        lea.readLong();//091我
        ret.direction = lea.readByte();
        /*
        System.out.println("连击数:"+ret.aranCombo);
        System.out.println("动作:"+ret.pos);
        System.out.println("姿势:"+ret.stance);
        System.out.println("速度:"+ret.speed);
        System.out.println("方向:"+ret.direction);
        */
        lea.skip(4);

        //这里是真正的攻击封包
        for (int i = 0; i < ret.numAttacked; i++) {
            int mobid = lea.readInt();
            //System.out.print("mobid: "+mobid+"\r\n");//怪物在地图的编号，并不是怪物ID
            lea.skip(14);
            List<Integer> allDamageNumbers = new ArrayList<Integer>();
            for (int j = 0; j < ret.numDamage; j++) {
                int damage = lea.readInt();//打怪的伤害
                //System.out.print("打怪伤害: "+damage+"\r\n");
                if(damage < 0 || damage >= 999999) {
                    savePacket = true;
                    chr.黄字公告("您的数据出现异常,请联系Gm处理。");
                }
                MapleStatEffect effect = null;
                if (ret.skill != 0) {
                    effect = SkillFactory.getSkill(ret.skill).getEffect(chr.getSkillLevel(SkillFactory.getSkill(ret.skill)));
                }
                if (damage != 0 && effect != null && effect.getFixedDamage() != 0) {
                    damage = effect.getFixedDamage();
                }
                allDamageNumbers.add(Integer.valueOf(damage));
            }
            lea.skip(8);
            ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(mobid), allDamageNumbers));
        }
        if(savePacket && !ret.allDamage.isEmpty()) {
            WriteToFile re = new WriteToFile("D:\\ErrorPacket\\RangeError.txt");
            re.WriteFile("damage："+ret.allDamage
                    + "\r\npacket："+lea.toString()
                    + "\r\nplayerName："+chr.getName()
                    + "\r\nskillId："+ret.skill
                    + "\r\nskillName："+SkillFactory.getSkillName(ret.skill)
                    + "\r\n"
                    );
            re.CloseFile();
            savePacket = false;
        }
        return ret;
    }

    //魔法攻击
    public AttackInfo parseMagic(MapleCharacter chr, LittleEndianAccessor lea) {
        //System.out.println("Magic 封包: "+lea.toString());
        AttackInfo ret = new AttackInfo();
        lea.readByte();//对几个怪有作用
        lea.readLong();
        ret.numAttackedAndDamage = lea.readByte();//打了多少次
        lea.readLong();
        ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
        ret.numDamage = ret.numAttackedAndDamage & 0xF;
        ret.allDamage = new ArrayList<Pair<Integer, List<Integer>>>();
        ret.skill = changeSkilliId(lea.readInt());  //技能ID
        ret.move = lea.readByte();//这是088新添加的 我还没分析,因为move需要声明 是新的变量
        lea.skip(1); //01
        lea.skip(8); //不知道是什么信息
        if(ret.move != 0) {
            lea.skip(4); //00
            Odinms(lea);
            Moveme(lea);
            lea.skip(17);
            //System.out.println("move != 0");
        } else {
            //System.out.println("move == 0");
        }
        //charge对应的是用技能的时候头上有能量槽
        if (ret.skill == 火毒.创世之破 || ret.skill == 冰雷.创世之破 || ret.skill == 牧师.创世之破 || ret.skill == 龙神.冰点寒气 || ret.skill == 龙神.火焰喷射) {
            ret.charge = lea.readInt();
        } else {
            ret.charge = 0;
        }
        lea.skip(4);
        ret.aranCombo = lea.readByte(); //这是目前的连击数?
        ret.pos = lea.readByte(); //动作
        ret.stance = lea.readByte(); //姿势
        lea.readInt();
        if(chr.getCygnusBless())
            lea.skip(12); //3个相同的Int
        ret.speed = lea.readByte();//速度
        lea.skip(1);
        lea.skip(4);
        lea.skip(4);//00
        /*
        System.out.println("skillid: "+ret.skill);
        System.out.println("连击数: "+ret.aranCombo);
        System.out.println("动作："+ret.pos);
        System.out.println("姿势："+ret.stance);
        System.out.println("武器攻击速度: "+ret.speed);
        */
        //这里是真正的攻击封包
        for (int i = 0; i < ret.numAttacked; i++) {
            int mobid = lea.readInt();
            lea.skip(14);
            //System.out.print("mobid: "+mobid+"\r\n");//怪物在地图的编号，并不是怪物ID
            List<Integer> allDamageNumbers = new ArrayList<Integer>();
            for (int j = 0; j < ret.numDamage; j++) {
                int damage = lea.readInt();//打怪的伤害
                //System.out.print("打怪伤害: "+damage+"\r\n");
                if(damage < 0 || damage >= 999999) {
                    savePacket = true;
                    chr.黄字公告("您的数据出现异常,请联系Gm处理。");
                }
                MapleStatEffect effect = null;
                if (ret.skill != 0) {
                    effect = SkillFactory.getSkill(ret.skill).getEffect(chr.getSkillLevel(SkillFactory.getSkill(ret.skill)));
                }
                if (damage != 0 && effect != null && effect.getFixedDamage() != 0) {
                    damage = effect.getFixedDamage();
                }
                allDamageNumbers.add(Integer.valueOf(damage));
            }
            lea.skip(8);
            ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(mobid), allDamageNumbers));
        }
        if(savePacket && !ret.allDamage.isEmpty()) {
            WriteToFile re = new WriteToFile("D:\\ErrorPacket\\MagicError.txt");
            re.WriteFile("damage："+ret.allDamage
                    + "\r\npacket："+lea.toString()
                    + "\r\nplayerName："+chr.getName()
                    + "\r\nskillId："+ret.skill
                    + "\r\nskillName："+SkillFactory.getSkillName(ret.skill)
                    + "\r\n"
                    );
            re.CloseFile();
            savePacket = false;
        }
        return ret;
    }

    //近身攻击
    public AttackInfo parseClose(MapleCharacter chr, LittleEndianAccessor lea) { //解析伤害数值
        //System.out.println("Close 封包: "+lea.toString());
        AttackInfo ret = new AttackInfo();
        lea.readByte();
        lea.readLong();
        ret.numAttackedAndDamage = lea.readByte();//打了多少次
        lea.readLong();
        ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
        ret.numDamage = ret.numAttackedAndDamage & 0xF;
        ret.allDamage = new ArrayList<Pair<Integer, List<Integer>>>();
        ret.skill = changeSkilliId(lea.readInt());  //技能ID
        ret.move = lea.readByte();//是否移动角色
        lea.skip(1); //01
        lea.skip(8); //不知道是什么信息
        if(ret.move != 0) {
            lea.skip(4); //00
            Odinms(lea);
            Moveme(lea);
            lea.skip(17);
            //System.out.println("move != 0");
        } else {
            //System.out.println("move == 0");
        }
        int sourceid = ret.skill;
        //斗气爆裂 连环吸血 终极投掷 幻影狼牙 钻石星辰 战神之盾
        if (sourceid == 战神.斗气爆裂
                || sourceid == 战神.连环吸血
                || sourceid == 战神.终极投掷
                || sourceid == 战神.幻影狼牙
                || sourceid == 战神.钻石星辰
                || sourceid == 战神.战神之盾)
            chr.setCombo(1);
        if (sourceid == 弓手.暴风箭雨
                || sourceid == 机械师.火焰喷射器
                || sourceid == 枪手.投弹攻击
                || sourceid == 夜行者.毒炸弹
                || sourceid == 拳手.贯骨击
                || sourceid == 奇袭者.贯骨击
                || sourceid == 双刀.终极斩
                || sourceid == 双刀.怪物炸弹
                || sourceid == 枪手.金属风暴
                || sourceid == 风灵使者.暴风箭雨
                ) {
            ret.charge = lea.readInt();
        } else {
            ret.charge = 0;
        }
        lea.skip(4);
        ret.aranCombo = lea.readByte(); //这是目前的连击数?
        ret.pos = lea.readByte(); //动作
        ret.stance = lea.readByte(); //姿势 0x80
        lea.readInt();//4位
        lea.readByte();
        if(chr.getCygnusBless())
            lea.skip(12); //3个相同的Int
        ret.speed = lea.readByte();//速度
        lea.skip(2);
        if (sourceid == 刀飞.金钱炸弹) { //金钱炸弹
            //System.out.println("金钱炸弹封包 :"+lea);
            return parseMesoExplosion(lea, ret);
        }
        lea.skip(2);
        lea.skip(4);//00
        /*
        System.out.println("skillid: "+sourceid);
        System.out.println("连击数: "+ret.aranCombo);
        System.out.println("动作："+ret.pos);
        System.out.println("姿势："+ret.stance);
        System.out.println("武器攻击速度: "+ret.speed);
        */
        //这里是真正的攻击封包
        for (int i = 0; i < ret.numAttacked; i++) {
            int mobid = lea.readInt();//怪物编号
            //System.out.print("mobid: "+mobid+"\r\n");//怪物在地图的编号，并不是怪物ID
            lea.skip(14);
            List<Integer> allDamageNumbers = new ArrayList<Integer>();
            for (int j = 0; j < ret.numDamage; j++) {
                int damage = lea.readInt();//打怪的伤害
                //System.out.print("打怪伤害: "+damage+"\r\n");
                if(damage < 0 || damage >= 999999) {
                    savePacket = true;
                    chr.黄字公告("您的数据出现异常,请联系Gm处理。");
                }
                MapleStatEffect effect = null;
                if (ret.skill != 0) {
                    effect = SkillFactory.getSkill(ret.skill).getEffect(chr.getSkillLevel(SkillFactory.getSkill(ret.skill)));
                }
                if (damage != 0 && effect != null && effect.getFixedDamage() != 0) {
                    damage = effect.getFixedDamage();
                }
                allDamageNumbers.add(Integer.valueOf(damage));
            }
            lea.skip(8);
            ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(mobid), allDamageNumbers));
        }
        if(savePacket && !ret.allDamage.isEmpty()) {
            WriteToFile re = new WriteToFile("D:\\ErrorPacket\\CloseError.txt");
            re.WriteFile("damage："+ret.allDamage
                    + "\r\npacket："+lea.toString()
                    + "\r\nplayerName："+chr.getName()
                    + "\r\nskillId："+ret.skill
                    + "\r\nskillName："+SkillFactory.getSkillName(ret.skill)
                    + "\r\n"
                    );
            re.CloseFile();
            savePacket = false;
        }
        return ret;
    }

    public AttackInfo parseMesoExplosion(LittleEndianAccessor lea, AttackInfo ret) {
        if (ret.numAttackedAndDamage == 0) { //没打怪
            //System.out.println("没打怪 只爆破钱");
            lea.skip(10);
            int bullets = lea.readByte();
            //System.out.println("钱的数量1: "+bullets);
            for (int j = 0; j < bullets; j++) {
                int mesoid = lea.readInt();
                //System.out.println("金钱oid1: "+mesoid);
                lea.skip(1);
                ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(mesoid), null));
            }
            return ret;
        } else {
            lea.skip(6);
            //System.out.println("打怪了");
        }
        //System.out.println("ret.numAttacked + 1的值(大循环多少次)："+ret.numAttacked + 1);
        for (int i = 0; i < ret.numAttacked + 1; i++) {
            //System.out.println("循环");
            int oid = lea.readInt();
            if (i < ret.numAttacked) {
                //System.out.println("怪物的oid: "+oid);
                lea.skip(12);
                int bullets = lea.readByte();
                //System.out.println("钱的数量2: "+bullets);
                List<Integer> allDamageNumbers = new ArrayList<Integer>();
                for (int j = 0; j < bullets; j++) {
                    int damage = lea.readInt();
                    //System.out.println("金钱炸伤害:  "+damage);
                    allDamageNumbers.add(Integer.valueOf(damage));
                }
                ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(oid), allDamageNumbers));
                lea.skip(8);
            } else {
                int bullets = lea.readByte();
                //System.out.println("钱的数量3: "+bullets);
                for (int j = 0; j < bullets; j++) {
                    int mesoid = lea.readInt();
                    //System.out.println("金钱oid2: "+mesoid);
                    lea.skip(1);
                    ret.allDamage.add(new Pair<Integer, List<Integer>>(Integer.valueOf(mesoid), null));
                }
            }
        }
        return ret;
    }

    private void Odinms(LittleEndianAccessor lea) {
        int numCommands = lea.readByte();//循环次数和移动次数
        //System.out.println("numCommands 的值 ："+numCommands);
        for (int i = 0; i < numCommands; i++) {
            int command = lea.readByte();//移动类型
            //System.out.println("command 的值 ："+command);
            switch (command) {
                case 0: // 正常移动
                case 17: // Float
                case 37: //龙神
                case 38: //龙神
                    lea.skip(17);
                    break;
                case 1:
                case 2: //怪物移动[小金人]
                case 6: // fj
                case 18: //龙卷风
                    lea.skip(7);
                    break;
                case 5: //093变更
                case 4: // tele... -.-
                case 7: // assaulter 攻击者?
                case 8: // 暗杀
                case 9: //093变更
                case 14: //瞬移
                    lea.skip(9);
                    break;
                case 10: // 换装备的 大巨变的版本
                    lea.skip(1);
                    break;
                case 3: //093变更
                case 11: // 椅子
                case 12: //原来是下跳 现在不知道是什么
                    lea.skip(9);
                    break;
                case 13:  //下跳 093由12更改为13
                    lea.skip(19);
                    break;
                case 15:
                case 16: //093
                case 20:
                case 21:
                case 22:
                case 23:
                case 24:
                case 25: //093
                case 26:
                case 27:
                case 28:
                    lea.skip(3);
                    break;
                default:
                    log.warn("没处理的 movement 包类型【{}】 【客户端发的包】", command);
                    log.warn("包内容: {}", lea.toString());
                    WriteToFile re = new WriteToFile("D:\\ErrorPacket\\MoveError3.txt");
                    re.WriteFile("unknowMoveType：["+command+"]");
                    //re.WriteFile("\r\n"+lea.toString());
                    re.CloseFile();
                    break;
            }
        }
    }

    private void Moveme(LittleEndianAccessor lea) {
        double skip = lea.readByte();
        skip = Math.ceil(skip / 2);
        lea.skip((int) skip);
    }

    private static int changeSkilliId(int skillID) {
        int changeID = skillID;
        switch (skillID) {
            case 21110007: //全力挥击
            case 21110008:
                changeID = 21110002;
                break;
            case 21120009: //战神之舞
            case 21120010:
                changeID = 21120002;
                break;
            case 32001008: //惩戒
            case 32001009:
            case 32001010:
            case 32001011:
                changeID = 32001001;
                break;
            case 35101010: //强化机枪扫射
                changeID = 35001004;
                break;
            case 35101009: //强化火焰喷射器
                changeID = 35001001;
                break;
            case 4321001: //龙卷风(攻击)
                changeID = 4321000;
                break;
            default:
                changeID = skillID;
                break;
        }
        
        return changeID;
    }

    private boolean isPartyBuff(int skill1) {
        //如果技能有范围 但是不是组队技能的话就在这里false
        switch(skill1){
            case 冰雷.快速移动精通:
            case 火毒.快速移动精通:
            case 牧师.快速移动精通:
            case 战法.快速移动精通:
                return true;
        }
        return false;
    }

}